/*
 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include <jni.h>
#include <stdio.h>
/* Changed from strings.h to string.h for Windows. */
#include <string.h>
#include <stdlib.h>
#include "jnihelper.h"

extern "C" {

#define DIGESTLENGTH 16

typedef struct {
    const char **str;
    char **checkstr;
} CHAR_ARRAY;

typedef struct {
    const jchar **str;
    char **checkstr;
    int *size;
} JCHAR_ARRAY;

JNIEXPORT jstring JNICALL
Java_nsk_stress_jni_JNIter001_jnistress (JNIEnv *env, jobject jobj, jstring jstr,
                    jint nstr, jint printperiod) {

    int i,j;
    size_t k;
    static CHAR_ARRAY *element;
    unsigned char digest[DIGESTLENGTH];
    static int allocs=0;
    static size_t strsize=0;
    static unsigned int compared=1;

    const char *clsName = "nsk/stress/jni/JNIter001";
    const char *name="setpass";
    const char *sig="(Z)V";
    const char *halt="halt";
    const char *haltSig="()V";
    jstring tmpstr;
    jclass clazz;
    jmethodID methodID;

    env->MonitorEnter(jobj); CE
    if (!allocs) {
        element = (CHAR_ARRAY *)c_malloc(env, sizeof(CHAR_ARRAY));
        element->str = (const char **)c_malloc(env, nstr*sizeof(const char *));
        element->checkstr = (char **)c_malloc(env, nstr*sizeof(char *));
        for (j=0;j<nstr;j++)
            element->checkstr[j] = (char *)c_malloc(env, DIGESTLENGTH*sizeof(char));
    }
    for(j=0;j<DIGESTLENGTH;j++) {
        digest[j]=0;
    }
    element->str[allocs] = env->GetStringUTFChars(jstr,0); CE
    if (strlen(element->str[allocs]) !=
        (size_t) env->GetStringUTFLength(jstr))
        printf("Length is wrong in string No. %d\n",allocs);
    else
        strsize += strlen(element->str[allocs])+1;
    for (k = 0; k < strlen(element->str[allocs]); k++) {
       digest[k % DIGESTLENGTH] += element->str[allocs][k];
    }
    memcpy(element->checkstr[allocs],digest,DIGESTLENGTH);
    allocs++;
    if (allocs%printperiod==0) {
        printf("Check string for thread %s is ", element->str[allocs-1]);
        for (j=0;j<DIGESTLENGTH;j++)
            printf("%02x", digest[j]);
        printf("\n");
    }
    if (allocs==nstr) {
        printf("JNI UTF8 strings memory=%zd\n", strsize);
        tmpstr=env->NewStringUTF(element->str[allocs-1]); CE
        for (j=0; j<nstr; j++) {

            for(i=0;i<DIGESTLENGTH;i++) {
                digest[i]=0;
            }
            for (k=0; k < strlen(element->str[j]); k++) {
                digest[k % DIGESTLENGTH] += element->str[j][k];
            }
            if (memcmp(digest,element->checkstr[j],DIGESTLENGTH)==0) {
                env->ReleaseStringUTFChars(jstr,element->str[j]); CE
                element->str[j] = NULL;
                element->checkstr[j] = NULL;
            }
            else {
                compared=0;
                printf("The element No. %d has been corrupted %s vs %s\n",j, element->str[j],element->checkstr[j]);
                printf("Digest string  is %s [", element->str[j]);
                for (i=0;i<DIGESTLENGTH;i++)
                    printf("ch[%d]=%02x", i, digest[i]);
                printf("Digest end\n");
            }
        }
        allocs=0;
        strsize = 0;
        for (j=0;j<nstr;j++)
            free(element->checkstr[j]);
        free(element->checkstr);
        free((void *)(element->str));
        free(element);
        clazz=env->FindClass(clsName); CE
        if (!compared) {
            methodID=env->GetStaticMethodID(clazz, name, sig); CE
            env->CallStaticVoidMethod(clazz, methodID, JNI_FALSE); CE
        }
        //methodID=env->GetStaticMethodID(clazz, halt, haltSig); CE
        //env->CallStaticVoidMethod(clazz, methodID); CE
        return(tmpstr);
    }
    env->MonitorExit(jobj); CE
    return (env->NewStringUTF(element->str[allocs-1]));
}

JNIEXPORT jstring JNICALL
Java_nsk_stress_jni_JNIter001_jnistress1(JNIEnv *env, jobject jobj, jstring jstr,
                    jint nstr, jint printperiod) {

    int i,j;
    static JCHAR_ARRAY *javachars;
    unsigned char digest[DIGESTLENGTH];
    static int index=0;
    static long len=0;
    static unsigned int equal=1;
    char *elem;
    int elem_len = -1;

    const char *clsName = "nsk/stress/jni/JNIter001";
    const char *name="setpass";
    const char *sig="(Z)V";
    const char *halt="halt";
    const char *haltSig="()V";
    jstring tmpstr;
    jclass clazz;
    jmethodID methodID;

    env->MonitorEnter(jobj); CE
    if (!index) {
        javachars = (JCHAR_ARRAY *)c_malloc(env, sizeof(JCHAR_ARRAY));
        javachars->str = (const jchar **)c_malloc(env, nstr*sizeof(const jchar *));
        javachars->checkstr = (char **)c_malloc(env, nstr*sizeof(char *));
        javachars->size = (int *)c_malloc(env, nstr*sizeof(int));
        for (j=0;j<nstr;j++)
            javachars->checkstr[j] = (char *)c_malloc(env, DIGESTLENGTH*sizeof(char));
    }
    for(j=0;j<DIGESTLENGTH;j++) {
        digest[j]=0;
    }
    javachars->str[index] = env->GetStringChars(jstr,0); CE
    javachars->size[index] = env->GetStringUTFLength(jstr); CE
    elem_len = javachars->size[index];
    len += elem_len;
    elem = (char*) c_malloc(env, elem_len*sizeof(char));
    for (j=0; j < elem_len; j++) {
        elem[j] = (char) javachars->str[index][j];
    }

    //memcpy(digest, elem, javachars->size[index]);
    for(j=0;j<elem_len; j++) {
        digest[j % DIGESTLENGTH]+=elem[j];
    }
    memcpy(javachars->checkstr[index++],digest,DIGESTLENGTH);
    if (index%printperiod==0) {
        printf("Check string sum for thread %.*s is ", elem_len, elem);
        for (j=0;j<DIGESTLENGTH;j++)
            printf("%02x", digest[j]);
        printf("\n");
    }
    free(elem);
    if (index==nstr) {
        printf("JNI Unicode strings memory=%ld\n",len);
        tmpstr=env->NewString(javachars->str[index-1],elem_len); CE
        for (j=0; j<nstr; j++) {
            elem = (char*) c_malloc(env, javachars->size[j]*sizeof(char));
            for (i=0; i < javachars->size[j]; i++) {
                elem[i] = (char) javachars->str[j][i];
            }
            //memcpy(digest, elem, javachars->size[j]);
            for(i=0;i<DIGESTLENGTH;i++) {
                digest[i]=0;
            }
            for(i=0;i<javachars->size[j]; i++) {
                digest[i % DIGESTLENGTH]+=elem[i];
            }
            free(elem);
            if (memcmp(digest,javachars->checkstr[j],javachars->size[j])==0) {
                env->ReleaseStringChars(jstr,javachars->str[j]); CE
                javachars->str[j] = NULL;
                javachars->checkstr[j] = NULL;
                javachars->size[j] = 0;
            }
            else {
                equal=0;
                printf("The Unicode element No. %d has been corrupted\n",j);
                for(i=0;i<DIGESTLENGTH;i++) {
                    printf("digest[%d]=%02x checkstr[%d]=%02x\n",i,digest[i],i,javachars->checkstr[j][i]);
                }
            }
        }
        index=0;
        len = 0;
        for (j=0;j<nstr;j++)
            free(javachars->checkstr[j]);
        free(javachars->checkstr);
        free((void *)(javachars->str));
        free(javachars->size);
        free(javachars);
        clazz=env->FindClass(clsName); CE
        if (!equal) {
            methodID=env->GetStaticMethodID(clazz, name, sig); CE
            env->CallStaticVoidMethod(clazz, methodID, JNI_FALSE); CE
        }
        //methodID=env->GetStaticMethodID(clazz, halt, haltSig); CE
        //env->CallStaticVoidMethod(clazz, methodID); CE
        return(tmpstr);
    }
    env->MonitorExit(jobj); CE
    return(env->NewString(javachars->str[index-1],elem_len));
}

}
